package com.hello.gen.util;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.hello.common.core.text.CharsetKit;

/**
 * 文件合并工具类
 * 
 * @author hello
 */
public class FileMergeUtils {
    private static final Logger log = LoggerFactory.getLogger(FileMergeUtils.class);
    
    /**
     * 将新内容与现有文件内容合并
     * 
     * @param file 现有文件
     * @param newContent 新内容
     * @return 合并后的内容
     * @throws IOException IO异常
     */
    public static String mergeContent(File file, String newContent) throws IOException {
        if (!file.exists()) {
            return newContent; // 文件不存在，直接使用新内容
        }
        
        String existingContent = FileUtils.readFileToString(file, CharsetKit.UTF_8);
        String fileExtension = getFileExtension(file.getName());
        
        // 根据文件类型选择合并策略
        if ("java".equalsIgnoreCase(fileExtension)) {
            return mergeJavaContent(existingContent, newContent);
        } else if ("xml".equalsIgnoreCase(fileExtension)) {
            return mergeXmlContent(existingContent, newContent);
        } else if ("vue".equalsIgnoreCase(fileExtension) || "js".equalsIgnoreCase(fileExtension)) {
            return mergeScriptContent(existingContent, newContent);
        } else {
            // 对于其他类型文件，保留原文件内容，并记录日志
            log.warn("文件 {} 已存在，无法合并未知类型的文件，保留原文件内容", file.getAbsolutePath());
            return existingContent;
        }
    }
    
    /**
     * 获取文件扩展名
     * 
     * @param fileName 文件名
     * @return 扩展名
     */
    private static String getFileExtension(String fileName) {
        int dotIndex = fileName.lastIndexOf('.');
        return (dotIndex == -1) ? "" : fileName.substring(dotIndex + 1);
    }
    
    /**
     * 合并Java文件内容
     * 
     * @param existingContent 现有内容
     * @param newContent 新内容
     * @return 合并后的内容
     */
    private static String mergeJavaContent(String existingContent, String newContent) {
        // 提取包声明和导入语句
        String existingPackage = extractPackageDeclaration(existingContent);
        String newPackage = extractPackageDeclaration(newContent);
        
        Set<String> existingImports = extractImports(existingContent);
        Set<String> newImports = extractImports(newContent);
        
        // 合并导入语句，保留所有不重复的导入
        Set<String> mergedImports = new HashSet<>(existingImports);
        mergedImports.addAll(newImports);
        
        // 提取类体
        String existingClassBody = extractClassBody(existingContent);
        String newClassBody = extractClassBody(newContent);
        
        // 合并类体，替换或添加方法
        String mergedClassBody = mergeClassBodies(existingClassBody, newClassBody);
        
        // 构建合并后的文件内容
        StringBuilder mergedContent = new StringBuilder();
        
        // 使用现有包声明（通常两者应该相同）
        mergedContent.append(existingPackage.isEmpty() ? newPackage : existingPackage).append("\n\n");
        
        // 添加所有导入语句
        for (String importStmt : mergedImports) {
            mergedContent.append(importStmt).append("\n");
        }
        mergedContent.append("\n");
        
        // 添加合并后的类体
        mergedContent.append(mergedClassBody);
        
        return mergedContent.toString();
    }
    
    /**
     * 提取包声明
     */
    private static String extractPackageDeclaration(String content) {
        Pattern pattern = Pattern.compile("package\\s+[^;]+;");
        Matcher matcher = pattern.matcher(content);
        return matcher.find() ? matcher.group(0) : "";
    }
    
    /**
     * 提取导入语句
     */
    private static Set<String> extractImports(String content) {
        Set<String> imports = new HashSet<>();
        Pattern pattern = Pattern.compile("import\\s+[^;]+;");
        Matcher matcher = pattern.matcher(content);
        while (matcher.find()) {
            imports.add(matcher.group(0));
        }
        return imports;
    }
    
    /**
     * 提取类体
     */
    private static String extractClassBody(String content) {
        int classStartIndex = content.indexOf("public class");
        if (classStartIndex == -1) {
            classStartIndex = content.indexOf("class ");
        }
        
        return classStartIndex != -1 ? content.substring(classStartIndex) : "";
    }
    
    /**
     * 合并类体
     * 简单策略：保留原有方法，添加新的方法和字段
     */
    private static String mergeClassBodies(String existingBody, String newBody) {
        // 这里实现简单的类体合并逻辑
        // 复杂的实现需要通过AST解析等方式完成
        
        // 提取现有类名和类声明部分
        Pattern classPattern = Pattern.compile("(public\\s+)?(class|interface|enum)\\s+\\w+[^{]*\\{");
        Matcher existingMatcher = classPattern.matcher(existingBody);
        if (!existingMatcher.find()) {
            // 如果无法解析现有类体，返回新类体
            return newBody;
        }
        
        String classDeclaration = existingMatcher.group(0);
        
        // 提取方法和字段
        List<String> existingMethods = extractMethods(existingBody);
        List<String> newMethods = extractMethods(newBody);
        
        // 合并方法（保留原有的，添加新的）
        Set<String> methodSignatures = new HashSet<>();
        List<String> mergedMethods = new ArrayList<>();
        
        // 添加现有方法
        for (String method : existingMethods) {
            String signature = extractMethodSignature(method);
            methodSignatures.add(signature);
            mergedMethods.add(method);
        }
        
        // 添加新方法（如果签名不重复）
        for (String method : newMethods) {
            String signature = extractMethodSignature(method);
            if (!methodSignatures.contains(signature)) {
                mergedMethods.add(method);
            }
        }
        
        // 重建类体
        StringBuilder mergedBody = new StringBuilder();
        mergedBody.append(classDeclaration).append("\n");
        
        // 添加所有方法
        for (String method : mergedMethods) {
            mergedBody.append("\n    ").append(method).append("\n");
        }
        
        // 添加类闭合括号
        mergedBody.append("\n}");
        
        return mergedBody.toString();
    }
    
    /**
     * 提取方法
     */
    private static List<String> extractMethods(String classBody) {
        List<String> methods = new ArrayList<>();
        
        // 查找方法定义开始
        Pattern methodPattern = Pattern.compile("(public|protected|private|static|\\s)+[\\w<>\\[\\]]+\\s+(\\w+)\\s*\\([^)]*\\)\\s*(throws\\s+[\\w\\.]+\\s*)?\\{");
        Matcher methodMatcher = methodPattern.matcher(classBody);
        
        while (methodMatcher.find()) {
            int methodStart = methodMatcher.start();
            
            // 找到方法体的结束括号
            int openBraces = 1;
            int methodEnd = methodMatcher.end();
            
            for (int i = methodEnd; i < classBody.length(); i++) {
                char c = classBody.charAt(i);
                if (c == '{') {
                    openBraces++;
                } else if (c == '}') {
                    openBraces--;
                    if (openBraces == 0) {
                        methodEnd = i + 1;
                        break;
                    }
                }
            }
            
            if (methodEnd > methodStart) {
                String method = classBody.substring(methodStart, methodEnd);
                methods.add(method);
            }
        }
        
        return methods;
    }
    
    /**
     * 提取方法签名
     */
    private static String extractMethodSignature(String method) {
        Pattern signaturePattern = Pattern.compile("(\\w+)\\s*\\([^)]*\\)");
        Matcher signatureMatcher = signaturePattern.matcher(method);
        return signatureMatcher.find() ? signatureMatcher.group(0) : "";
    }
    
    /**
     * 合并XML文件内容
     * 
     * @param existingContent 现有内容
     * @param newContent 新内容
     * @return 合并后的内容
     */
    private static String mergeXmlContent(String existingContent, String newContent) {
        // 简单XML合并策略：保留原有的XML结构，添加新的XML节点
        // 更复杂的实现需要使用XML解析库
        
        // 检查是否是Mapper XML
        if (existingContent.contains("<mapper") && newContent.contains("<mapper")) {
            return mergeMapperXml(existingContent, newContent);
        }
        
        // 对于其他类型的XML，保留原有内容
        log.warn("XML文件已存在，使用简单保留策略");
        return existingContent;
    }
    
    /**
     * 合并MyBatis Mapper XML文件
     */
    private static String mergeMapperXml(String existingContent, String newContent) {
        try {
            // 提取根节点结束标记
            int rootEndIndex = existingContent.lastIndexOf("</mapper>");
            if (rootEndIndex == -1) {
                // 不是有效的mapper文件，返回原内容
                log.warn("现有内容不是有效的mapper XML文件，保留原文件内容");
                return existingContent;
            }
            
            // 安全检查：确保XML内容有mapper标签
            int mapperStartIndex = existingContent.indexOf("<mapper");
            int mapperEndTagIndex = existingContent.indexOf(">", mapperStartIndex);
            
            if (mapperStartIndex == -1 || mapperEndTagIndex == -1) {
                log.warn("无法找到有效的mapper标签，保留原文件内容");
                return existingContent;
            }
            
            // 提取SQL语句节点
            List<String> existingSqlNodes = extractXmlNodes(existingContent);
            List<String> newSqlNodes = extractXmlNodes(newContent);
            
            // 合并SQL节点（避免重复节点）
            Set<String> nodeIds = new HashSet<>();
            List<String> mergedNodes = new ArrayList<>();
            
            for (String node : existingSqlNodes) {
                String id = extractNodeId(node);
                if (!id.isEmpty()) {
                    nodeIds.add(id);
                }
                mergedNodes.add(node);
            }
            
            for (String node : newSqlNodes) {
                String id = extractNodeId(node);
                if (!id.isEmpty() && !nodeIds.contains(id)) {
                    mergedNodes.add(node);
                }
            }
            
            // 重建mapper文件
            StringBuilder mergedContent = new StringBuilder();
            
            // 使用更安全的方法提取头部内容
            String header = existingContent.substring(0, mapperStartIndex + "<mapper".length());
            String mapperAttributes = existingContent.substring(mapperStartIndex + "<mapper".length(), mapperEndTagIndex + 1);
            
            mergedContent.append(header).append(mapperAttributes).append("\n\n");
            
            // 添加所有SQL节点
            for (String node : mergedNodes) {
                mergedContent.append("    ").append(node).append("\n\n");
            }
            
            mergedContent.append("</mapper>");
            
            return mergedContent.toString();
        } catch (Exception e) {
            log.error("合并Mapper XML文件失败，保留原文件内容", e);
            return existingContent;
        }
    }
    
    /**
     * 提取XML节点
     */
    private static List<String> extractXmlNodes(String content) {
        List<String> nodes = new ArrayList<>();
        
        // 查找常见的MyBatis节点
        String[] nodeTypes = {"select", "insert", "update", "delete", "sql", "resultMap"};
        
        for (String nodeType : nodeTypes) {
            Pattern pattern = Pattern.compile("<" + nodeType + "[^>]*>.*?</" + nodeType + ">", Pattern.DOTALL);
            Matcher matcher = pattern.matcher(content);
            
            while (matcher.find()) {
                nodes.add(matcher.group(0));
            }
        }
        
        return nodes;
    }
    
    /**
     * 提取XML节点ID
     */
    private static String extractNodeId(String node) {
        Pattern pattern = Pattern.compile("id=[\"']([^\"']+)[\"']");
        Matcher matcher = pattern.matcher(node);
        return matcher.find() ? matcher.group(1) : "";
    }
    
    /**
     * 合并脚本文件内容（JS、Vue）
     * 
     * @param existingContent 现有内容
     * @param newContent 新内容
     * @return 合并后的内容
     */
    private static String mergeScriptContent(String existingContent, String newContent) {
        // 简单脚本合并策略：保留原有内容
        log.warn("脚本文件(JS/Vue)已存在，保留原有内容");
        return existingContent;
    }
    
    /**
     * 智能文件写入 - 如果文件存在则合并，不存在则直接写入
     * 
     * @param file 文件对象
     * @param content 内容
     * @throws IOException IO异常
     */
    public static void writeStringToFileWithMerge(File file, String content) throws IOException {
        // 确保目录存在
        File parentDir = file.getParentFile();
        if (parentDir != null && !parentDir.exists()) {
            parentDir.mkdirs();
        }
        
        // 如果文件存在，进行合并操作
        if (file.exists()) {
            log.info("文件已存在，准备合并: {}", file.getAbsolutePath());
            try {
                String mergedContent = mergeContent(file, content);
                FileUtils.writeStringToFile(file, mergedContent, CharsetKit.UTF_8);
                log.info("文件合并成功: {}", file.getAbsolutePath());
            } catch (Exception e) {
                log.error("文件合并失败，保留原文件内容: {}, 错误: {}", file.getAbsolutePath(), e.getMessage());
                // 文件合并失败，保留原文件不做修改
            }
        } else {
            // 文件不存在，直接写入
            log.info("创建新文件: {}", file.getAbsolutePath());
            FileUtils.writeStringToFile(file, content, CharsetKit.UTF_8);
        }
    }
} 